SPRDT-1011: migrate listing courtscheduler calls to reshaped Phase-1 contract#13
Open
ozturkmenb wants to merge 28 commits into
Open
SPRDT-1011: migrate listing courtscheduler calls to reshaped Phase-1 contract#13ozturkmenb wants to merge 28 commits into
ozturkmenb wants to merge 28 commits into
Conversation
Update listing's courtscheduler client to the Phase-1 reshaped API:
- search-by-id: GET /courtschedule/search.court-schedules-by-id -> GET
/sessions; query param courtScheduleIds -> ids
- release slots: DELETE /hearingslots/{id} -> DELETE /sessions/{id};
action remove.hearing.slots -> release.sessions
- list hearings: PUT /list/hearingslots -> POST /hearings; action
list.hearings-in-court-sessions -> list.hearings-in-sessions
- extend multiday: POST /extendmultidayhearing/hearingslots -> PATCH
/hearings/{hearingId} (new HttpPatch helper)
- search-and-book (mags/crown/crown-fallback): GET query endpoints ->
POST /hearings/{hearingId} with typed JSON body, actions
mags.search.and.book / crown.search.and.book
Response parsing: mags searchAndBook now reads sessions[0] (was a
hearingSlots object); multiday reads sessions[] (was courtSchedules[]).
Crown-fallback parser unchanged - matched by the courtscheduler
single-day flat-field change. Other responses serialise identical
domain objects, so their parsing is unchanged.
- CourtSchedulerServiceStub (WireMock): retarget every stub to the new
endpoints/actions — GET /sessions, DELETE /sessions/{id}
(release.sessions), POST /hearings (list.hearings-in-sessions),
PATCH /hearings/{id} (extend.multiday.hearing), POST /hearings/{id}
(mags/crown.search.and.book). Multiday vs crown-fallback share the
crown.search.and.book endpoint and are disambiguated by a
durationInMinutes body matcher (>360 vs <=360). Response bodies
reshaped: mags now {hearingId,sessions[]}, multiday courtSchedules->
sessions, crown-fallback flat fields + empty sessions[].
- HearingSlotsServiceTest: expect new URLs/methods/headers; DELETE now
202 ACCEPTED; HttpPatch for extend; POST bodies for search-and-book.
- CourtScheduleEnrichmentServiceTest + 3 search-book fixtures: new
sessions[] response shape for mags/multiday.
- HearingSlotsService: guard extendMultiDayHearing against empty payload
(was NPE on getString(hearingId) before the validation guard).
Both changed modules green (listing-common 165, listing-command-api 295).
The list.hearings-in-sessions REQUEST body is {"hearingSlots":[...]} (only
the RESPONSE uses "hearings"). The stub retarget mistakenly changed the
WireMock request-body matcher to containing("hearings"), which is not a
substring of "hearingSlots", so the real stub stopped matching and the
empty catch-all responded {"hearings":[]}. Enrichment then threw
IllegalStateException(Missing courtScheduleIds) -> 500 on every
list-court-hearing, cascading to 103 IT failures via the shared
whenCaseIsSubmittedForListing setup step. Restore the matcher to
containing("hearingSlots").
…r-call migration S1192: add SESSION_START_TIME constant in CourtScheduleEnrichmentService (4 literal uses). S3776: reduce postSearchBook cognitive complexity by extracting buildTypedJsonBody helper. Duplication: unify post/patch/postSearchBook execute+response logic via executeAndBuildResponse. Coverage: add unit tests for patch success/error/IOException, postSearchBook typed body (durationInMinutes as number, isPolice as boolean, hearingId in path, crown content-type), buildTypedJsonBody edge cases, and searchAndBookSlots paths (empty sessions, non-OK, draft/isDraft fallback, judiciaries present/absent).
Two request-body shape bugs surfaced against the real courtscheduler on
ns-ste-ccm-22 (both masked by loosely-matching WireMock IT stubs):
1. list.hearings-in-sessions sent hearingSlots[].ids instead of
courtScheduleIds - the COURT_SCHEDULE_IDS constant was repurposed to
"ids" for the GET /sessions query param but also built the list body
key. Split into COURT_SCHEDULE_IDS ("courtScheduleIds", body key) and
IDS_PARAM ("ids", query param).
2. multiDaySearchAndBook omitted courtCentreId + hearingDate, which
crown.search.and.book requires (additionalProperties:false). Supply
them from the hearing context at both call sites; the engine ignores
them for the anchored path but schema validation requires them present.
Harden CourtSchedulerServiceStub to match on the required nested keys
(courtScheduleId for list; courtCentreId + hearingDate for multiday crown)
so a missing-required-key regression fails the ITs instead of silently
passing. Add unit tests for both body shapes.
…ale draft id on update mergeCourtScheduleIdsFromNonDefaultDays previously skipped hearingDays that already carried a courtScheduleId, so on a reschedule the old draft id was left in place. The downstream fetch then returned the draft session, isDraft=true propagated to the aggregate, canAllocateForCrown() was closed, and hearing-rescheduled was emitted with allocated:false. Fix: remove the early-return guard and allow the nonDefaultDays id to overwrite the stale draft id when they differ. The new (non-draft) id is then fetched, isDraft=false is propagated, canAllocateForCrown() opens, and hearing-allocated-for-listing-v2 + hearing-rescheduled(allocated:true) are both emitted correctly. CROWN-only path (mergeCourtScheduleIdsFromNonDefaultDays is only called from enrichCrownUpdateHearing); MAGS behaviour unchanged.
…day window The J05 fix commit (205 new lines) shrank SonarQube's NUMBER_OF_DAYS=1 new-code denominator, flipping new_maintainability_rating to D by surfacing pre-existing smells inside the new-code period. Clear all 20 counted smells; every change is behaviour-preserving: CourtScheduleEnrichmentService (production): - S3358: extract nested ternaries into locals (startTime fallback, anchor centre id, anchor hearing date, isDraft resolution via if/else) - S6201: use instanceof pattern variables for JsonObject casts - S1125: simplify the CROWN isPolice boolean expression Tests (CourtScheduleEnrichmentServiceTest, ListingCommandApiTest, HearingAggregateTest): - S5786: drop redundant public on JUnit5 test methods - S6204: Stream.toList() instead of collect(Collectors.toList()) - S1117: rename locals that shadowed fields - S1128: remove unused import Unit tests green: CourtScheduleEnrichmentServiceTest 140, ListingCommandApiTest 33, HearingAggregateTest 173, 0 failures.
… (J05) Adds CrownUpdateHearingSingleDayIT: a CROWN, originally-unallocated single-day hearing updated via update-hearing-for-listing with a non-draft courtScheduleId on nonDefaultDays must end ALLOCATED (polls listing.search.hearing for $.allocated==true and the new $.startDate). Locks the end-to-end single-day CROWN allocation outcome (the J05 scenario) at the integration layer. The update-hearing-for-listing schema is additionalProperties:false and does not expose hearingDays, so the exact stale-draft-id merge collision remains covered by the unit/aggregate tests (CourtScheduleEnrichmentServiceTest, HearingAggregateTest); this IT locks the e2e outcome. Stubs: stubSearchCourtSchedulesByIdSession(isDraft=false) + stubListHearingInCourtSessionsForCourtSchedule; dates via ItClock.plusWorkingDays. Passes in the full listing IT suite (Tests run: 1, 0 failures).
…troom from resolved final schedule update-hearing-for-listing with only a courtScheduleId on nonDefaultDays (no room fields) booked the session but never allocated: the handler resolved a null command-level courtroom, called removeCourtRoom, and canAllocateForCrown() stayed closed. Allocation only worked when the caller (UI) also supplied the room. Fix: inverse of stripRoomInfoIfAnyDraft (ADR-005) — when the command carries no courtroom and every enriched hearingDay resolved to a FINAL (isDraft=false) session in one distinct room, promote that room to the command level (and backfill a roomless selectedCourtCentre, which the handler prefers for CROWN). Draft sessions are roomless on every courtscheduler query path so they can never qualify; payload-supplied rooms are never overridden; multi-day spans across different rooms stay underived. Locked failing-first at both levels: CourtScheduleEnrichmentServiceTest (3 new derivation tests red->green + draft/parity/mixed-room pins, 146/146) and CrownUpdateHearingScheduleOnlyIT (schedule-only allocate + ADR-005 draft mirror, red 90s-timeout on allocated=false -> green 2/2).
…t be resolved When the CROWN update enrichment could not resolve or book the requested sessions (multi-day search or single-day fetch returning empty), it logged a WARN and returned the seeded hearingDays unchanged — carrying an unverified courtScheduleId and a payload room. The aggregate then allocated the hearing while courtscheduler's bookings were untouched, diverging the two services (observed live on ns-ste-ccm-34: multi-day allocate collapsed to a 1-day allocated hearing while all three draft bookings remained). Fix: markDaysDraftWhenSessionsUnresolved — on either empty-result path the enriched days are marked isDraft=true, so canAllocateForCrown() stays closed, stripRoomInfoIfAnyDraft (ADR-005) clears day rooms downstream, and the courtScheduleIds are preserved for traceability. Two failing-first unit tests lock the behaviour; the two tests pinning the old return-unchanged contract were retargeted. Full listing IT suite green (239 tests).
…k ignores the requested start date A crown.search.and.book response whose block starts on a different date than the command requested is no longer silently adopted: the days are re-marked draft (same fail-safe as unresolved sessions) so the hearing window cannot diverge from the caller's intent.
Author
…s block total + anchor - validate CROWN update payload shape (CrownNonDefaultDaysValidator, 400 on violation): at most one virtual=true nonDefaultDay; startDate must equal the virtual day's date; genuine (non-virtual) days must fall within startDate..endDate - multi-day booking uses the virtual day's duration as the block TOTAL and its courtScheduleId/date as the anchor sent to courtscheduler, instead of summing every day (a mixed proxy+genuine payload double-counted, e.g. 1440+360=1800, and over-booked a 5th session past the requested endDate) - after session extraction, hearingDays whose date matches a genuine nonDefaultDay take that day's startTime (list-response times previously always won)
…eduler, CROWN listing-side, past-only)
Adds listing.command.move-hearing-to-past-date as a new content-type action on the
existing POST /hearings/{hearingId} resource. ListingCommandApi resolves the hearing
from the listing viewstore (new HearingLookupService, mirroring the existing
command.service.HearingService query-view lookup pattern) and rejects unknown
hearingIds with 422 HEARING_ID_NOT_FOUND before anything is sent.
MAGISTRATES hearings call courtscheduler synchronously via
CourtSchedulerServiceAdapter.moveHearingToPastDate (new HearingSlotsService POST to
/hearings/{hearingId}, application/vnd.courtscheduler.move-hearing-to-past-date+json);
a 2xx response enriches the command with a purpose-built MoveHearingToPastDateResult
and the enriched handler applies it via the existing
raiseHearingDayCourtSchedulesUpdated/HearingDayCourtScheduleUpdated event. A non-2xx
response raises MoveHearingToPastDateException, rendered as 422/404 by the new
MoveHearingToPastDateExceptionMapper (registered via the new
ListingCommandCommonProviders @specializes DefaultCommonProviders) - no event is sent.
CROWN hearings never call courtscheduler (Baris decision D1): the command-api
validates past-only (422 FUTURE_DATE_NOT_ALLOWED) and the enriched handler re-dates
the hearing purely listing-side by reusing the existing Hearing.changeStartDate
aggregate method (StartDateChangedForHearing event) - no new domain event needed.
Permission: PermissionConstants.createChangeHearingToPastDatePermission() (object
"Change hearing to past date", action "Link") plus a DRL rule using
userAndGroupProvider.hasPermission(...).
Adds MoveHearingToPastDateIT plus CourtSchedulerServiceStub/WireMockStubUtils
extensions and MAGS/CROWN test-data fixtures.
…ission DRL path in move IT
…m in MoveHearingToPastDateIT
…(changeStartDate + assignHearingDaysV2) - the court-schedule event alone cannot re-date a day
…s (S3776), reuse adapter constants (S1192), transient responseBody (S1948)
…t from the hearing's own room/time on the past date
… requires it); carry existing endTime for CROWN
…04 normalised); hearingId dropped from command body (path param only)
…s as update-hearing-for-listing - drops the unseeded 'Change hearing to past date'/Link permission (PermissionConstants removed, move IT uses the standard group stub)
…11 contract line
Cherry-picking the pasthearings feature (built on the release snapshot) onto
dev/SPRDT-1011-listing-callers (reshaped Phase-1 courtscheduler contract) left
a few merge artefacts that git could not flag and that only exist on the merged
result:
- HearingSlotsService: both lines declared HEARINGS_RESOURCE with different
values ("/hearings" vs "/hearings/") at different offsets, so the merge kept
both -> duplicate field. Unified on the SPRDT-1011 convention ("/hearings")
and made the move call append "/" + hearingId like every other call site.
- HearingSlotsService: restored the java.net.URL import that SPRDT-1011 dropped
when it migrated its own call sites to URIBuilder (the move method uses new URL).
- CourtSchedulerServiceAdapter.parseMoveHearingToPastDateResult: reuse the
existing COURT_SCHEDULE_ID / SESSION_DATE / SESSION_START_TIME / SESSION_END_TIME
constants instead of duplicating the string literals (Sonar S1192) — those
values are already declared as constants on the SPRDT-1011 line.
Whole reactor compiles (main + test sources); 248 ITs pass.
…(path-only)
The courtscheduler crown.search.and.book / mags.search.and.book request schemas
were reshaped to additionalProperties:false with hearingId removed (SPRDT-1089 /
team-pasthearings SPRDT-985 contract). hearingId now travels only in the
/hearings/{hearingId} path and is injected by the courtscheduler REST adapter.
HearingSlotsService.postSearchBook still built the body from ALL params, including
hearingId, so every CROWN search-and-book (list-new-hearing, update-hearing-for-
listing, MCC, adjournment) was rejected 400 by the strict schema -> allocation
aborted (CrownFallbackInvalidRequestException) -> CrownScheduledListingIT failed.
Exclude hearingId from the search-and-book body, matching the move-hearing-to-past-
date caller which already follows the reshaped contract. Adds a regression guard.
SPRDT-1011 vs SPRDT-1089 contract skew fix.
…he selected court centre, not a court-schedule id In enrichCrownUpdateHearing the courtCentreId query param passed to multiDaySearchAndBook fell back to anchorCourtScheduleId when hearing.getCourtCentreId() was null — a court-schedule UUID, not a court-centre UUID, so courtscheduler received a bogus courtCentreId (400 / mis-booking). Fall back to the hearing's own selected court centre (getSelectedCourtCentre().getId()) instead, mirroring the fallback pattern in the sibling handleCrownMultiDayEnrichment.
…earingId} path (follow c95cf7f) c95cf7f removed hearingId from the crown/mags search-and-book request body (now path-only, to satisfy courtscheduler's additionalProperties:false schema) but left the MAGS search-and-book WireMock stubs matching on `containing("hearingId":"<id>")` in the body. Post-change the body no longer carries hearingId, so those 5 stubs stopped matching -> "returned no sessions" -> hearings never allocated -> PayloadBasedListCourtHearingIT, ExhibitScenarioIT and PayloadBasedListNextHearingIT timed out on the viewstore poll (7 errors). Scope the 5 stubs by the hearingId in the URL path (/hearings/{hearingId}), mirroring the existing DELETE /sessions/{hearingId} stub, and drop the stale body matcher. Per-hearing scoping is preserved. The businessType body matcher and the PATCH/extend stubs (whose body still carries hearingId) are unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

SPRDT-1011 — migrate listing's courtscheduler calls to the reshaped Phase-1 contract
Updates
cpp-context-listing's courtscheduler client to the resource-based contract from the courtscheduler Phase-1 reshape (SPRDT-1089). In-place migration — no additive endpoints.Calls migrated
GET /courtschedule/search.court-schedules-by-id, paramcourtScheduleIdsGET /sessions, paramidsDELETE /hearingslots/{id}·remove.hearing.slotsDELETE /sessions/{id}·release.sessionsPUT /list/hearingslots·list.hearings-in-court-sessionsPOST /hearings·list.hearings-in-sessionsPOST /extendmultidayhearing/hearingslotsPATCH /hearings/{id}GETquery endpointsPOST /hearings/{id}with JSON body ·mags.search.and.book/crown.search.and.bookUnchanged (not in Phase-1 scope):
get.hearing.slots,get.hearing.ids,validate-session-availability,provisionalBooking.Response handling
searchBookSlots(mags) now readssessions[0](was ahearingSlots{}object).multiDaySearchAndBookreadssessions[](wascourtSchedules[]).Dependency
Requires the courtscheduler Phase-1 change that makes single-day
crown.search.and.bookcarry the booked session detail (room/time/draft/overbooked) flat on the response — ondev/SPRDT-1089-phase1-engine(courtscheduler PR #845). The integrated environment needs both this PR and that change deployed together.Verification
mvn clean install— full reactor green (26 modules)../runIntegrationTests.sh— 236 passed, 0 failed, 3 skipped.Notes
extendMultiDayHearingreturned a 500 (NPE) on an empty payload instead of aDataValidationException.